using System;
using System.Collections.Generic;
using System.Threading.Tasks;
using Admin.NET.Application.Entity.WxMP;
using Admin.NET.Application.Service.Mp.Dto;
using Admin.NET.Core;
using Furion.DataEncryption;
using Furion.DependencyInjection;
using Furion.DynamicApiController;
using Furion.FriendlyException;
using Mapster;
using Microsoft.AspNetCore.Authorization;
using Microsoft.AspNetCore.Mvc;
using NewLife.Caching;
using SKIT.FlurlHttpClient;
using SKIT.FlurlHttpClient.Wechat.Api;
using SKIT.FlurlHttpClient.Wechat.Api.Models;
using SqlSugar;

namespace Admin.NET.Application.Service.Mp;

/// <summary>
/// 公众号相关接口
/// </summary>
[ApiDescriptionSettings(new string[] { "业务应用" }, Order = 100)]
public class MpOpenService : IDynamicApiController, ITransient, IPrivateDependency
{
	private readonly SqlSugarRepository<MpManage> _rep;

	private readonly SqlSugarRepository<Owner> _repOwner;

	private readonly ICache _cache;

	public MpOpenService(SqlSugarRepository<MpManage> rep, SqlSugarRepository<Owner> repOwner, ICache cache)
	{
		_rep = rep;
		_repOwner = repOwner;
		_cache = cache;
	}

	/// <summary>
	/// 利用授权code获取openid
	/// </summary>
	/// <param name="code"></param>
	/// <returns></returns>
	[HttpGet("/MpOpen/GetOpenIdWithCode")]
	[AllowAnonymous]
	[NonUnify]
	public async Task<IActionResult> GetOpenIdWithCode([FromQuery] string code, [FromQuery] string appId)
	{
		(WechatApiClient, string) tuple = await CreateWechatApiClient(appId, null);
		WechatApiClient wechat = tuple.Item1;
		string bindUrl = tuple.Item2;
		SnsOAuth2AccessTokenRequest request = new SnsOAuth2AccessTokenRequest
		{
			Code = code
		};
		SnsOAuth2AccessTokenResponse res = await wechat.ExecuteSnsOAuth2AccessTokenAsync(request);
		if (res.ErrorCode != 0)
		{
			throw Oops.Oh(res.ErrorMessage + res.ErrorCode);
		}
		int count = await (from w in _repOwner.AsQueryable()
			where w.OpenId == res.OpenId
			select w).CountAsync();
		string url = bindUrl + "?openid=" + res.OpenId + "&count=" + count + "&appid=" + appId;
		return new RedirectResult(url);
	}

	private async Task<(WechatApiClient, string)> CreateWechatApiClient(string appId, MpManage data)
	{
		if (data == null)
		{
			data = await (from w in _rep.AsQueryable()
				where w.WechatAppId == appId
				select w).FirstAsync();
		}
		if (data == null)
		{
			throw Oops.Bah("微信公众号未配置");
		}
		WechatApiClient wechatApiClient = new WechatApiClient(new WechatApiClientOptions
		{
			AppId = data.WechatAppId,
			AppSecret = data.WechatAppSecret
		});
		wechatApiClient.Configure(delegate(CommonClientSettings settings)
		{
			settings.JsonSerializer = new FlurlNewtonsoftJsonSerializer();
		});
		return (wechatApiClient, data.BindUrl);
	}

	/// <summary>
	/// 公众号绑定
	/// </summary>
	/// <param name="dto"></param>
	/// <returns></returns>
	[HttpPost("/MpOpen/BindMp")]
	[AllowAnonymous]
	public async Task BindMp([FromBody] BindMpDto dto)
	{
		string encryptPasswod = MD5Encryption.Encrypt(dto.Password);
		Owner user = await _repOwner.AsQueryable().Filter(null, isDisabledGobalFilter: true).FirstAsync((Owner u) => u.PhoneNumber.Equals(dto.PhoneNumber) && u.Password.Equals(encryptPasswod));
		if (user == null)
		{
			throw Oops.Oh(ErrorCodeEnum.D1000);
		}
		if (!string.IsNullOrWhiteSpace(user.OpenId))
		{
			throw Oops.Bah("该账号已被绑定");
		}
		user.OpenId = dto.OpenId;
		user.AppId = dto.AppId;
		_repOwner.Context.Updateable(user).UpdateColumns((Owner it) => new { it.OpenId, it.AppId }).ExecuteCommand();
	}

	/// <summary>
	/// 解绑微信
	/// </summary>
	/// <param name="openId"></param>
	/// <returns></returns>
	[HttpPost("/MpOpen/UnBindMp")]
	[AllowAnonymous]
	public async Task UnBindMp([FromQuery] string openId)
	{
		Owner user = await _repOwner.AsQueryable().Filter(null, isDisabledGobalFilter: true).FirstAsync((Owner u) => u.OpenId.Equals(openId));
		if (user == null)
		{
			throw Oops.Oh("该账号未被绑定");
		}
		user.OpenId = null;
		user.AppId = null;
		_repOwner.Context.Updateable(user).UpdateColumns((Owner it) => new { it.OpenId, it.AppId }).ExecuteCommand();
	}

	/// <summary>
	/// 分页查询公众号配置
	/// </summary>
	/// <param name="input"></param>
	/// <returns></returns>
	[HttpGet("/MpOpen/page")]
	public async Task<dynamic> Page([FromQuery] MpOpenInput input)
	{
		ISugarQueryable<MpManage> query = _rep.Context.Queryable<MpManage>().WhereIF(!string.IsNullOrWhiteSpace(input.Name), (MpManage u) => u.Name.Contains(input.Name.Trim())).WhereIF(!string.IsNullOrWhiteSpace(input.WechatAppId), (MpManage u) => u.WechatAppId == input.WechatAppId.Trim());
		query = query.OrderBuilder(input);
		return await query.ToPagedListAsync(input.Page, input.PageSize);
	}

	/// <summary>
	/// 增加公众号配置
	/// </summary>
	/// <param name="input"></param>
	/// <returns></returns>
	[HttpPost("/MpOpen/add")]
	public async Task Add(AddMpOpenInput input)
	{
		if (await (from w in _rep.AsQueryable()
			where w.WechatAppId == input.WechatAppId
			select w).CountAsync() > 0)
		{
			throw Oops.Bah("该公众号已配置，无需重复添加");
		}
		MpManage entity = input.Adapt<MpManage>();
		await _rep.InsertAsync(entity);
	}

	/// <summary>
	/// 删除公众号
	/// </summary>
	/// <param name="input"></param>
	/// <returns></returns>
	[HttpPost("/MpOpen/delete")]
	public async Task Delete(DeleteMpOpenInput input)
	{
		MpManage entity = await _rep.GetFirstAsync((MpManage u) => u.Id == input.Id);
		await _rep.DeleteAsync(entity);
	}

	/// <summary>
	/// 更新公众号配置
	/// </summary>
	/// <param name="input"></param>
	/// <returns></returns>
	[HttpPost("/MpOpen/edit")]
	public async Task Update(UpdateMpOpenInput input)
	{
		MpManage entity = input.Adapt<MpManage>();
		await _rep.UpdateAsync(entity);
	}

	/// <summary>
	/// 获取公众号列表
	/// </summary>
	/// <param name="input"></param>
	/// <returns></returns>
	[HttpGet("/MpOpen/list")]
	public async Task<dynamic> List()
	{
		return await _rep.AsQueryable().ToListAsync();
	}

	/// <summary>
	/// 分页查询公众号消息模板
	/// </summary>
	/// <param name="input"></param>
	/// <returns></returns>
	[HttpGet("/MpOpen/templatepage")]
	public async Task<dynamic> TemplatePage([FromQuery] TemplateInput input)
	{
		ISugarQueryable<MpTemplate> query = from w in _rep.Context.Queryable<MpTemplate>()
			where w.AppId == input.AppId
			select w;
		query = query.OrderBuilder(input);
		return await query.ToPagedListAsync(input.Page, input.PageSize);
	}

	/// <summary>
	/// 增加公众号消息模板配置
	/// </summary>
	/// <param name="input"></param>
	/// <returns></returns>
	[HttpPost("/MpOpen/templateadd")]
	public async Task TemplateAdd(AddTemplateInput input)
	{
		if (await (from w in _rep.Context.Queryable<MpTemplate>()
			where w.TemplateId == input.TemplateId
			select w).CountAsync() > 0)
		{
			throw Oops.Bah("该模板已配置，无需重复添加");
		}
		if (await (from w in _rep.Context.Queryable<MpTemplate>()
			where w.AppId == input.AppId && w.Type == input.Type
			select w).CountAsync() > 0)
		{
			throw Oops.Bah("该类型已配置，请核对后添加");
		}
		MpTemplate entity = input.Adapt<MpTemplate>();
		switch (entity.Type)
		{
		case 0:
			entity.Name = "欠费账单提醒";
			break;
		case 1:
			entity.Name = "缴费成功通知";
			break;
		case 2:
			entity.Name = "缴费失败通知";
			break;
		case 3:
			entity.Name = "送电成功提醒";
			break;
		case 4:
			entity.Name = "送电失败提醒";
			break;
		case 5:
			entity.Name = "断电成功提醒";
			break;
		case 6:
			entity.Name = "设备断电失败提醒";
			break;
		case 7:
			entity.Name = "用户账单逾期提醒";
			break;
		}
		await _rep.Context.Insertable(entity).ExecuteCommandAsync();
	}

	/// <summary>
	/// 删除公众号模板配置
	/// </summary>
	/// <param name="input"></param>
	/// <returns></returns>
	[HttpPost("/MpOpen/templatedelete")]
	public async Task TemplateDelete(DeleteTemplateInput input)
	{
		MpTemplate entity = await (from u in _rep.Context.Queryable<MpTemplate>()
			where u.Id == input.Id
			select u).FirstAsync();
		await _rep.Context.Deleteable(entity).ExecuteCommandAsync();
	}

	/// <summary>
	/// 更新公众号模板配置
	/// </summary>
	/// <param name="input"></param>
	/// <returns></returns>
	[HttpPost("/MpOpen/templateedit")]
	public async Task TemplateUpdate(UpdateTemplateInput input)
	{
		MpTemplate entity = input.Adapt<MpTemplate>();
		await _rep.Context.Updateable(entity).ExecuteCommandAsync();
	}

	/// <summary>
	/// 获取账号信息
	/// </summary>
	/// <param name="ownerId"></param>
	/// <returns></returns>
	private async Task<Owner> GetOwner(long ownerId)
	{
		return await (from w in _rep.Context.Queryable<Owner>()
			where w.Id == ownerId
			select w).FirstAsync();
	}

	/// <summary>
	/// 获取公众号配置信息
	/// </summary>
	/// <param name="appId"></param>
	/// <returns></returns>
	private async Task<MpManage> GetMpManage(string appId)
	{
		return await (from w in _rep.Context.Queryable<MpManage>()
			where w.WechatAppId == appId
			select w).FirstAsync();
	}

	/// <summary>
	/// 获取模板
	/// </summary>
	/// <param name="appId"></param>
	/// <param name="type"></param>
	/// <returns></returns>
	private async Task<MpTemplate> GetTemplate(string appId, int type)
	{
		return await (from w in _rep.Context.Queryable<MpTemplate>()
			where w.AppId == appId && w.Type == type
			select w).FirstAsync();
	}

	/// <summary>
	/// 获取mptoken
	/// </summary>
	/// <param name="client"></param>
	/// <param name="mp"></param>
	/// <returns></returns>
	private async Task<string> GetMpToken(WechatApiClient client, MpManage mp)
	{
		string key = "Wechat:" + mp.WechatAppId;
		if (_cache.ContainsKey(key))
		{
			return _cache.Get<string>(key);
		}
		CgibinTokenRequest request = new CgibinTokenRequest();
		CgibinTokenResponse res = await client.ExecuteCgibinTokenAsync(request);
		if (res.ErrorCode != 0)
		{
			throw Oops.Oh(res.ErrorMessage + res.ErrorCode);
		}
		_cache.Set(key, res.AccessToken, res.ExpiresIn - 120);
		return res.AccessToken;
	}

	private async Task SendMessageTemplate(long ownerId, int type, Dictionary<string, CgibinMessageTemplateSendRequest.Types.DataItem> data)
	{
		Owner owner = await GetOwner(ownerId);
		if (owner == null || string.IsNullOrWhiteSpace(owner.AppId) || string.IsNullOrWhiteSpace(owner.OpenId))
		{
			return;
		}
		MpManage wechat = await GetMpManage(owner.AppId);
		if (wechat == null)
		{
			return;
		}
		MpTemplate template = await GetTemplate(owner.AppId, type);
		if (template == null)
		{
			return;
		}
		(WechatApiClient, string) tuple = await CreateWechatApiClient(owner.AppId, wechat);
		var (client, _) = tuple;
		_ = tuple.Item2;
		string token = await GetMpToken(client, wechat);
		if (!string.IsNullOrWhiteSpace(token))
		{
			CgibinMessageTemplateSendRequest request = new CgibinMessageTemplateSendRequest
			{
				AccessToken = token,
				TemplateId = template.TemplateId,
				ToUserOpenId = owner.OpenId,
				MiniProgram = new CgibinMessageTemplateSendRequest.Types.MiniProgram
				{
					AppId = wechat.MiniProgramAppId
				},
				Data = data
			};
			CgibinMessageTemplateSendResponse res = await client.ExecuteCgibinMessageTemplateSendAsync(request);
			if (res.ErrorCode != 0)
			{
				throw Oops.Oh(res.ErrorMessage + res.ErrorCode);
			}
		}
	}

	/// <summary>
	/// 欠费账单提醒，余额提醒
	/// </summary>
	/// <param name="roomName"></param>
	/// <param name="roomId"></param>
	/// <param name="ownerId"></param>
	/// <returns></returns>
	public async Task SendBalanceTemplate(string roomName, long roomId, long ownerId, string amount)
	{
		string now = DateTime.Now.ToString("yyyy-MM-dd HH:mm:ss");
		Dictionary<string, CgibinMessageTemplateSendRequest.Types.DataItem> data = new Dictionary<string, CgibinMessageTemplateSendRequest.Types.DataItem>
		{
			{
				"thing3",
				new CgibinMessageTemplateSendRequest.Types.DataItem
				{
					Value = roomName
				}
			},
			{
				"amount4",
				new CgibinMessageTemplateSendRequest.Types.DataItem
				{
					Value = amount
				}
			},
			{
				"time6",
				new CgibinMessageTemplateSendRequest.Types.DataItem
				{
					Value = now
				}
			}
		};
		await SendMessageTemplate(ownerId, 0, data);
	}

	/// <summary>
	/// 缴费成功通知
	/// </summary>
	/// <param name="roomName"></param>
	/// <param name="roomId"></param>
	/// <param name="ownerId"></param>
	/// <returns></returns>
	public async Task SendPaySuccessTemplate(long ownerId, string account, string amount, string balance, string orderNo)
	{
		string now = DateTime.Now.ToString("yyyy-MM-dd HH:mm:ss");
		Dictionary<string, CgibinMessageTemplateSendRequest.Types.DataItem> data = new Dictionary<string, CgibinMessageTemplateSendRequest.Types.DataItem>
		{
			{
				"number2",
				new CgibinMessageTemplateSendRequest.Types.DataItem
				{
					Value = account
				}
			},
			{
				"amount3",
				new CgibinMessageTemplateSendRequest.Types.DataItem
				{
					Value = amount
				}
			},
			{
				"amount16",
				new CgibinMessageTemplateSendRequest.Types.DataItem
				{
					Value = balance
				}
			},
			{
				"time4",
				new CgibinMessageTemplateSendRequest.Types.DataItem
				{
					Value = now
				}
			},
			{
				"character_string5",
				new CgibinMessageTemplateSendRequest.Types.DataItem
				{
					Value = orderNo
				}
			}
		};
		await SendMessageTemplate(ownerId, 1, data);
	}

	/// <summary>
	/// 缴费失败通知
	/// </summary>
	/// <param name="roomName"></param>
	/// <param name="roomId"></param>
	/// <param name="ownerId"></param>
	/// <returns></returns>
	public async Task SendPayFailedTemplate(string roomName, long roomId, long ownerId, string account, string amount, string reason)
	{
		string now = DateTime.Now.ToString("yyyy-MM-dd HH:mm:ss");
		Dictionary<string, CgibinMessageTemplateSendRequest.Types.DataItem> data = new Dictionary<string, CgibinMessageTemplateSendRequest.Types.DataItem>
		{
			{
				"number2",
				new CgibinMessageTemplateSendRequest.Types.DataItem
				{
					Value = account
				}
			},
			{
				"amount3",
				new CgibinMessageTemplateSendRequest.Types.DataItem
				{
					Value = amount
				}
			},
			{
				"time4",
				new CgibinMessageTemplateSendRequest.Types.DataItem
				{
					Value = now
				}
			},
			{
				"thing5",
				new CgibinMessageTemplateSendRequest.Types.DataItem
				{
					Value = reason
				}
			}
		};
		await SendMessageTemplate(ownerId, 2, data);
	}

	/// <summary>
	/// 送电成功通知
	/// </summary>
	/// <param name="roomName"></param>
	/// <param name="roomId"></param>
	/// <param name="ownerId"></param>
	/// <param name="meterName"></param>
	/// <returns></returns>
	public async Task SendPowerOnSuccessTemplate(string roomName, long ownerId)
	{
		string now = DateTime.Now.ToString("yyyy-MM-dd HH:mm:ss");
		Dictionary<string, CgibinMessageTemplateSendRequest.Types.DataItem> data = new Dictionary<string, CgibinMessageTemplateSendRequest.Types.DataItem>
		{
			{
				"time2",
				new CgibinMessageTemplateSendRequest.Types.DataItem
				{
					Value = now
				}
			},
			{
				"thing3",
				new CgibinMessageTemplateSendRequest.Types.DataItem
				{
					Value = roomName
				}
			}
		};
		await SendMessageTemplate(ownerId, 3, data);
	}

	/// <summary>
	/// 送电失败通知
	/// </summary>
	/// <param name="roomName"></param>
	/// <param name="roomId"></param>
	/// <param name="ownerId"></param>
	/// <param name="meterName"></param>
	/// <returns></returns>
	public async Task SendPowerOnFailedTemplate(string roomName, long roomId, long ownerId, string meterName, string result)
	{
		string now = DateTime.Now.ToString("yyyy-MM-dd HH:mm:ss");
		Dictionary<string, CgibinMessageTemplateSendRequest.Types.DataItem> data = new Dictionary<string, CgibinMessageTemplateSendRequest.Types.DataItem>
		{
			{
				"time2",
				new CgibinMessageTemplateSendRequest.Types.DataItem
				{
					Value = now
				}
			},
			{
				"thing3",
				new CgibinMessageTemplateSendRequest.Types.DataItem
				{
					Value = result
				}
			},
			{
				"thing4",
				new CgibinMessageTemplateSendRequest.Types.DataItem
				{
					Value = meterName
				}
			},
			{
				"thing6",
				new CgibinMessageTemplateSendRequest.Types.DataItem
				{
					Value = roomName
				}
			}
		};
		await SendMessageTemplate(ownerId, 4, data);
	}

	/// <summary>
	/// 断电成功通知
	/// </summary>
	/// <param name="roomName"></param>
	/// <param name="roomId"></param>
	/// <param name="ownerId"></param>
	/// <param name="meterName"></param>
	/// <returns></returns>
	public async Task SendPowerOffSuccessTemplate(string roomName, long ownerId)
	{
		string now = DateTime.Now.ToString("yyyy-MM-dd HH:mm:ss");
		Dictionary<string, CgibinMessageTemplateSendRequest.Types.DataItem> data = new Dictionary<string, CgibinMessageTemplateSendRequest.Types.DataItem>
		{
			{
				"time2",
				new CgibinMessageTemplateSendRequest.Types.DataItem
				{
					Value = now
				}
			},
			{
				"thing3",
				new CgibinMessageTemplateSendRequest.Types.DataItem
				{
					Value = roomName
				}
			}
		};
		await SendMessageTemplate(ownerId, 5, data);
	}

	/// <summary>
	/// 断电失败通知
	/// </summary>
	/// <param name="roomName"></param>
	/// <param name="roomId"></param>
	/// <param name="ownerId"></param>
	/// <param name="meterName"></param>
	/// <returns></returns>
	public async Task SendPowerOffFailedTemplate(string roomName, long roomId, long ownerId, string meterName, string result)
	{
		string now = DateTime.Now.ToString("yyyy-MM-dd HH:mm:ss");
		Dictionary<string, CgibinMessageTemplateSendRequest.Types.DataItem> data = new Dictionary<string, CgibinMessageTemplateSendRequest.Types.DataItem>
		{
			{
				"time2",
				new CgibinMessageTemplateSendRequest.Types.DataItem
				{
					Value = now
				}
			},
			{
				"thing3",
				new CgibinMessageTemplateSendRequest.Types.DataItem
				{
					Value = result
				}
			},
			{
				"thing4",
				new CgibinMessageTemplateSendRequest.Types.DataItem
				{
					Value = meterName
				}
			},
			{
				"thing6",
				new CgibinMessageTemplateSendRequest.Types.DataItem
				{
					Value = roomName
				}
			}
		};
		await SendMessageTemplate(ownerId, 6, data);
	}

	/// <summary>
	/// 自定账单通知
	/// </summary>
	/// <param name="billName">账单类型，默认自定账单</param>
	/// <param name="amount">金额，结尾可带元</param>
	/// <param name="status">状态：默认待支付</param>
	/// <param name="endTime">截止日期</param>
	/// <param name="ownerId">推送用户</param>
	/// <param name="method">超期方法</param>
	/// <returns></returns>
	public async Task SendCustomBillTemplate(string billName, string amount, string status, string endTime, long ownerId, string method)
	{
		Dictionary<string, CgibinMessageTemplateSendRequest.Types.DataItem> data = new Dictionary<string, CgibinMessageTemplateSendRequest.Types.DataItem>
		{
			{
				"short_thing1",
				new CgibinMessageTemplateSendRequest.Types.DataItem
				{
					Value = billName
				}
			},
			{
				"amount2",
				new CgibinMessageTemplateSendRequest.Types.DataItem
				{
					Value = amount
				}
			},
			{
				"thing3",
				new CgibinMessageTemplateSendRequest.Types.DataItem
				{
					Value = status
				}
			},
			{
				"time7",
				new CgibinMessageTemplateSendRequest.Types.DataItem
				{
					Value = endTime
				}
			},
			{
				"const8",
				new CgibinMessageTemplateSendRequest.Types.DataItem
				{
					Value = method
				}
			}
		};
		await SendMessageTemplate(ownerId, 7, data);
	}
}
